package yieldCurve;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;

import com.csvreader.*;

public class InstrumentData {
	ArrayList<Cash> cash = new ArrayList<Cash>(5);
	ArrayList<Swap> swaps = new ArrayList<Swap>(5);
	ArrayList<FRA> fras = new ArrayList<FRA>(5);
	ArrayList<Instrument> ins = new ArrayList<Instrument>(5);
	CsvReader dataInputCsv;
	CsvReader specCsv;

	/** Constructor
	 * 
	 * @param curvedata Filename of yield curve data
	 * @param curvespec Filename of yield curve specification
	 */
	public InstrumentData(String curvedata, String curvespec) {
		read(curvedata, curvespec);
	}
	
	public void read(String curvedata, String curvespec) {
		/** Read in CSV files. Put swaps in swaps[], fras in fras[], cash in cash[]. */
		String instrumentType;
		String subType;
		double[] market = new double[20];
		int i = 0;
		int rec = 0;
		Instrument tempInstrument;

		boolean exist3M = false;
		boolean exist3x6 = false;
		boolean exist6x9 = false;
		boolean exist1Y = false;
		// Only 3M, 6M, 1Y matter in building yield curve
		try {
			dataInputCsv = new CsvReader(curvedata);
			dataInputCsv.readHeaders();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			throw new Error("Data Input file not found");
		} catch (IOException e) {
			e.printStackTrace();
			throw new Error("Data Input I/O error");
		}
		try {
			specCsv = new CsvReader(curvespec);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			throw new Error("Spec file not found");
		}
		try {
			while (dataInputCsv.readRecord()) {
				market[i++] = Double.parseDouble(dataInputCsv.get("Rate")) / 100;
			}
			
			i = 0;
			rec = 0;
			specCsv.readHeaders();
			while (specCsv.readRecord()) {
				instrumentType = specCsv.get("InstrumentType");
				subType = specCsv.get("subType");
				if (instrumentType.equalsIgnoreCase("CASH")) {
					tempInstrument = new Cash(subType, market[i++]);
					cash.add((Cash) tempInstrument);
					ins.add(tempInstrument);
					if (subType.equalsIgnoreCase("3M")) {
						exist3M = true;
					}
				} else if (instrumentType.equalsIgnoreCase("FRA")) {
					tempInstrument = new FRA(subType, market[i++]);
					fras.add((FRA) tempInstrument);
					ins.add(tempInstrument);
					if (subType.equalsIgnoreCase("3x6")) {
						exist3x6 = true;
					}
					if (subType.equalsIgnoreCase("6x9")) {
						exist6x9 = true;
					}
				} else if (instrumentType.equalsIgnoreCase("SWAP")) {
					tempInstrument = new Swap(subType, market[i++]);
					swaps.add((Swap) tempInstrument);
					ins.add(tempInstrument);
					if (subType.equalsIgnoreCase("1Y")) {
						exist1Y = true;
					}
				}
				rec++;
			}
			
			// Check missing value, bootstrap if necessary
			// check cash 3M
			if (exist3M == false) {
				// bootstrap 3M
				double marketRate = YieldCurve.interpolate(cash.get(cash.size() - 1).toMonth, cash.get(cash.size() - 1).marketRate,
						fras.get(0).toMonth, fras.get(0).marketRate, 3);
				cash.add(new Cash("3M", marketRate));
			}
			
			// check FRA 3x6
			if (exist3x6 == false) {
				int idx2x5 = 0;
				int idx6x9 = 0;
				for (i = 0; i < fras.size(); i++) {
					if (fras.get(i).subType.equalsIgnoreCase("2x5")) {
						idx2x5 = i;
					}
					if (fras.get(i).subType.equalsIgnoreCase("6x9")) {
						idx6x9 = i;
					}
				}
				double marketRate = YieldCurve.interpolate(fras.get(idx2x5).toMonth, fras.get(idx2x5).marketRate, 
						fras.get(idx6x9).toMonth, fras.get(idx6x9).marketRate, 6);
				fras.add(idx2x5 + 1, new FRA("3x6", marketRate));
			}
			
			// check FRA 6x9
			if (exist6x9 == false) {
				double marketRate = YieldCurve.interpolate(fras.get(fras.size() - 1).toMonth, fras.get(fras.size() - 1).marketRate, 
						swaps.get(0).toMonth, swaps.get(0).marketRate, 9);
				fras.add(new FRA("6x9", marketRate));
			}
			
			// check FRA 1Y
			if (exist1Y == false) {
				double marketRate = YieldCurve.interpolate(fras.get(fras.size() - 1).toMonth, fras.get(fras.size() - 1).marketRate, 
						swaps.get(0).toMonth, swaps.get(0).marketRate, 12);
				swaps.add(0, new Swap("1Y", marketRate));
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			throw new Error("File not found");
		} catch (IOException e) {
			e.printStackTrace();
			throw new Error("I/O error");
		} catch (NumberFormatException e) {
			throw new Error("NumberFormatException: Input file may be corrupt.");
		}
	}
	public Swap getSwap(int index) {
		return swaps.get(index);
	}
	public FRA getFRA(int index) {
		return fras.get(index);
	}
	public Cash getCash(int index) {
		return cash.get(index);
	}
	
	public ArrayList<Cash> getCashList() {
		return cash;
	}
	
	public ArrayList<FRA> getFRAList() {
		return fras;
	}
	
	public ArrayList<Swap> getSwapList() {
		return swaps;
	}
	
	/** test method for instrumentdata class
	 * 
	 * @param args none supported
	 */
	public static void main(String[] args) {
		@SuppressWarnings("unused")
		InstrumentData instru = new InstrumentData("curveDataInput-lack3M.csv", "curveSpec-lack3M.csv");
	}

}